class ANAPIWrapper
  attr_accessor :id, :data
  class_inheritable_accessor :wrapped
  
  GATEWAY = ActiveMerchant::Billing::AuthorizeNetCimGateway.new :login => 'xxx', :password => 'xxx'
  self.wrapped = nil
  
  def method_missing(method, *args, &blk)
    @data.send(method, *args, &blk)
  end
  
  def wrapped
    self.class.wrapped
  end

  def self.create(data, options={})
    item = new(data, nil, true, options)
    item.save
    item
  end

  def initialize(data = {}, id = nil, new_record=true, options={})
    if data.is_a? Hash
      data = wrapped.new(data)
    end
    
    @data = data
    @id = id
    @new_record = new_record
  end

  def new_record?
    @new_record
  end
  
  def valid? 
    @data && @data.valid?
  end
  
  def full_aggregate_errors
    @data && @data.full_aggregate_errors
  end
  
  def errors
    @data && @data.errors
  end
  
  def self.schema
    wrapped.schema
  end
  
  def data=(data)
    @data = APIWrapper === data ? data : wrapped.new(data)
  end

end

class ANProfile < ANAPIWrapper

  self.wrapped = ActiveMerchant::Billing::AuthorizeNetCimAPI::CustomerProfile

  def self.find(id)
    res = GATEWAY.get_profile(id)
    if res.success?
      new(res.params['data'], id, false)
    else
      raise "Find Error: #{res.message}"
    end
  end
  
  def save
    res = if new_record?
            GATEWAY.create_profile(@data)
          else
            GATEWAY.update_profile(@id, @data)
          end

    if res.success?
      if new_record?
        @id = res.params['data'][:customerProfileId]
        @new_record = false
      end
      return true
    else
      errors.add_to_base(res.message)
      return false
    end
  end
  
  def update
    res = GATEWAY.update_profile(@id)
    res.success?
  end
  
  def destroy
    res = GATEWAY.delete_profile(@id)
    if res.success?
      true
    else
      raise "Destroy Error: #{res.message}"
    end
  end
  
  def find_shipping(id)
    ShippingAddress.find(id, :profile_id => @id)
  end
  
  def find_profile(id)
    PaymentProfile.find(id, :profile_id => @id)
  end
  
end

class ANShippingAddress < ANAPIWrapper
  attr_accessor :profile_id
  
  self.wrapped = ActiveMerchant::Billing::AuthorizeNetCimAPI::ShipToList  
  
  def self.find(id, options={})
    res = GATEWAY.get_shipping_address(options[:profile_id], id)
    if res.success?
      new(res.params['data'], id, false, options)
    else
      raise "Find Error: #{res.message} #{res.params.inspect}"
    end
  end
  
  def initialize(data=nil, id=nil, new_record=true, options={})
    @profile_id = options[:profile_id]
    super
  end
  
  def save
    res = if new_record?
            GATEWAY.create_shipping_address(profile_id, @data)
          else
            GATEWAY.update_shipping_address(profile_id, @data)
          end

    if res.success?
      if new_record?
        @id = res.params['data'][:customerAddressId]
        @new_record = false
      end
      return true
    else
      errors.add_to_base(res.message)
      return false
    end
  end
  
  def destroy
    res = GATEWAY.delete_shipping_address(profile_id, @id)
    if res.sucess?
      true
    else
      raise "Destroy Error: #{res.message}"
    end
  end
  
  def profile
    @profile ||= (new_record? ? nil : Profile.find(profile_id))
  end
  
end

class ANPaymentProfile < ANAPIWrapper
  attr_accessor :profile_id
  
  @wrapped = ActiveMerchant::Billing::AuthorizeNetCimAPI::PaymentProfile  
  
  def self.find(id, options={})
    res = GATEWAY.get_payment_profile(options[:profile_id], id)
    if res.success?
      new(res.params['data'], id, false, options)
    else
      raise "Find Error: #{res.message} #{res.params.inspect}"
    end
  end
  
  def initialize(data=nil, id=nil, new_record=true, options={})
    @profile_id = options[:profile_id]
    super
  end
  
  def save
    res = if new_record?
            GATEWAY.create_payment_profile(profile_id, @data)
          else
            GATEWAY.update_payment_profile(profile_id, @data)
          end

    if res.success?
      if new_record?
        @id = res.params['data'][:customerPaymentProfileId]
        @new_record = false
      end
      return true
    else
      errors.add_to_base(res.message)
      return false
    end
  end
  
  def destroy
    res = GATEWAY.delete_payment_profile(profile_id, @id)
    if res.success?
      true
    else
      raise "Destroy Error: #{res.message}"
    end
  end
  
  def profile
    @profile ||= (new_record? ? nil : Profile.find(profile_id))
  end
end